package com.wechat.bot.common.file;

import com.wechat.bot.common.contants.Constants;
import com.wechat.bot.common.log.Log;
import org.apache.commons.lang3.ArrayUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName: FileUtil
 * @Description: 文件工具类
 * @author: wangzunpeng
 * @date: 2018年10月31日 下午7:46:44
 */
public class FileUtil {
    /**
     * 传入路径，返回是否是绝对路径，是绝对路径返回true，反之
     *
     * @param path
     * @return
     */
    public static boolean isAbsolutePath(String path) {
        if (path.startsWith("/") || path.indexOf(":") > 0) {
            return true;
        }
        return false;
    }

    /**
     * @param pathList
     * @param pathWithRegex 带有正则表达式的路径，路径中的正则表达式以两个“#”包起来
     * @return void @throws
     * @Title: ListFilePath
     * @Description: 根据正则表达式获取真正的路径
     */
    public static void listFilePath(List<String> pathList, String pathWithRegex) {

        String reg = Constants.TWO_FLAG_REGEX.getValue(); // 目标目录中存在以#开始和结尾的路径，中间是正则表达式，从文件名中截取对应的字符替换成路径
        Matcher m = Pattern.compile(reg).matcher(pathWithRegex);

        if (m.find()) {

            String[] paths = pathWithRegex.split(reg);

            String headPath = paths[0];
            String realRegex = m.group(1); // 路径正则表达式

            File file = new File(headPath);
            if (file.exists()) {

                File[] files = file.listFiles();

                for (File f : files) {
                    String fileName = f.getName();
                    if (Pattern.matches(realRegex, fileName)) {
                        String tmpPath = Pattern.compile(reg).matcher(pathWithRegex).replaceFirst(fileName);
                        listFilePath(pathList, tmpPath);
                    }
                }
            } else {
                // System.out.println("路径:" + headPath + "不存在!");
                Log.warn(headPath + " 不存在!");
            }
        } else {
            pathList.add(validFilePath(pathWithRegex));
        }
    }

    /**
     * @Title: ValidFilePath @Description: 格式化路径，前后增加斜杠 @param path 需要格式化的路径 @return
     * String 格式化后的路径 @throws
     */
    public static String validFilePath(String path) {
        path = formatPath(path);
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        if (!path.endsWith("/")) {
            path = path + "/";
        }
        return path;
    }

    /**
     * @Title: FormatPath @Description: 格式化路径，反斜杠转正斜杠，多斜杠转单一斜杠 @param path
     * 需要格式化的路径 @return String 格式化后的路径 @throws
     */
    public static String formatPath(String path) {
        if (path.indexOf("//") != -1) {
            path = formatPath(path.replace("//", "/"));
        }
        if (path.indexOf("\\\\") != -1) {
            path = formatPath(path.replace("\\\\", "\\"));
        }
        if (path.indexOf("\\") != -1) {
            path = formatPath(path.replace("\\", "/"));
        }
        return path;
    }

    /**
     * 写数据到文件的方法
     *
     * @param content  内容
     * @param filename 文件名称
     * @param isappend 是否把 内容 追加到文件里
     */
    public static void writeString(String content, String filename, boolean isappend) {
        String tmpFileName = filename + ".tmp";
        File tmpFile = new File(tmpFileName);

        File file = new File(filename);
        String parent = file.getParent();
        File pFile = new File(parent);

        if (!pFile.exists()) {
            pFile.mkdirs();
        }

        BufferedWriter bufferedWriter = null;
        try {
            bufferedWriter = new BufferedWriter(new FileWriter(tmpFileName, isappend));
            bufferedWriter.write(content);

            boolean res = tmpFile.renameTo(file);
            if (res) {
                Log.info("Rename to real filename success: " + tmpFileName + " >> " + filename);
            } else {
                Log.info("Rename to real filename failed: " + tmpFileName + " >> " + filename);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (bufferedWriter != null) {
                try {
                    bufferedWriter.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    /**
     * 读取文本文件，按行将内容存入list返回，默认使用utf-8编码
     *
     * @param file
     * @return list 按行存入的list
     */
    public static List<String> readTxtFileContent(File file) {
        return readTxtFileContent(file, "UTF-8");
    }

    /**
     * 读取文本文件，按行将内容存入list返回
     *
     * @param file
     * @param encode 文件编码
     * @return list 按行存入的list
     */
    public static List<String> readTxtFileContent(File file, String encode) {
        List<String> list = new ArrayList<String>();
        InputStreamReader isr = null;
        BufferedReader br = null;
        try {
            isr = new InputStreamReader(new FileInputStream(file), encode);
            br = new BufferedReader(isr);
            String str = null;
            while ((str = br.readLine()) != null) {
                if (!str.trim().equals("")) {
                    list.add(str);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                isr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return list;
    }

    public static String readTxtFileContent(String fileName) {
        return readTxtFileContent(fileName, "UTF-8");
    }

    public static String readTxtFileContent(String fileName, String encode) {
        // String encoding = "UTF-8";
        File file = new File(fileName);
        if (!file.exists()) {
            Log.error("文件不存在: " + fileName);
            return null;
        }
        Long fileLength = file.length();
        byte[] fileContent = new byte[fileLength.intValue()];
        FileInputStream in = null;
        String contents = null;
        try {
            in = new FileInputStream(file);
            in.read(fileContent);
            contents = new String(fileContent, encode);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return contents;
        // try {
        // return new String(filecontent, encoding);
        // } catch (UnsupportedEncodingException e) {
        // System.err.println("The OS does not support " + encoding);
        // e.printStackTrace();
        // return null;
        // }
    }

    /**
     * @Title: DeleteFiles @Description: 删除早于保留期限之前的文件及空目录 @param path
     * 需要删除数据的路径 @param time 保留时间，单位：毫秒 @throws
     */
    public static void deleteFiles(String path, long time) {
        path = validFilePath(path);
        File file = new File(path);
        long nowTime = Calendar.getInstance().getTime().getTime();
        if (file.exists()) {
            long lastModified = file.lastModified();
            if (file.isFile()) {
                // 如果文件存在时间大于需要保留的时间，则删除
                if (time < nowTime - lastModified) {
                    file.delete();
                }
            } else {
                File[] files = file.listFiles();
                if (files != null && files.length > 0) {
                    for (File f : files) {
                        String fname = f.getName();
                        deleteFiles(path + fname, time);
                    }
                } else {
                    if ((nowTime - lastModified) > time) {
                        file.delete();
                    }
                }
            }
        }
    }

    /**
     * @Title: DeleteFiles @Description: 删除文件及空目录 @param path 需要删除数据的路径 @throws
     */
    public static void deleteFiles(String path) {
        path = validFilePath(path);
        File file = new File(path);
        if (file.exists()) {
            if (file.isFile()) {
                // 如果文件存在时间大于需要保留的时间，则删除
                file.delete();
            } else {
                File[] files = file.listFiles();
                if (files != null && files.length > 0) {
                    for (File f : files) {
                        String fname = f.getName();
                        deleteFiles(path + fname);
                    }
                } else {
                    file.delete();
                }
            }
        }
    }

    /**
     * @param path     带有表达式的路径 :
     *                 /usr/data/dump/#${filename}.split(\"~\")[0].split(\".\")[1].substring(0)#/#${filename}.split(\"~\")[0]#/#${filename}.split(\"~\")[1]#
     * @param fileName 文件名 :
     *                 A.0001.0032.R001~Z_SURF_C_BCUQ_20171226183120_O_AWS_FTM_PQC.txt
     * @return 转换后的路径 :
     * /usr/data/dump/0001/A.0001.0032.R001/Z_SURF_C_BCUQ_20171226183120_O_AWS_FTM_PQC.txt
     * @Title: getRpath
     * @Description: 转换存在表达式如：#${filename}.split("*")[x].substring(1,3)#的路径，
     * @throws:
     */
    public static String getRpath(String path, File file) {
        String fileName = file.getName();

        String reg = Constants.TWO_FLAG_REGEX.getValue(); // 目标目录中存在以#开始和结尾的路径，中间是正则表达式，从文件名中截取对应的字符替换成路径
        Matcher m = Pattern.compile(reg).matcher(path);

        while (m.find()) {

            String realRegex = m.group(1); // 带有split或substring的表达式
            String newRegex = realRegex;
            String newFileName = fileName;
            if (realRegex.contains(PARENT_FILE_NAME_REG)) {
                newRegex = realRegex.replace(PARENT_FILE_NAME_REG, FILE_NAME_REG);
                newFileName = file.getParentFile().getName();
            }
            List<String> res = new ArrayList<String>();
            formatString(newFileName, newRegex, res);

            path = path.replaceFirst("#" + string2Regex(realRegex) + "#", res.get(res.size() - 1));
        }
        return path;
    }

    public final static String FILE_NAME_REG = "${filename}";
    public final static String PARENT_FILE_NAME_REG = "${parentfilename}";

    public final static String SP_CHARACTER = "\\(\"(.*?)\"\\)";
    public final static String SP_DIGIT = "\\[(\\d+?)\\]";
    public final static String SUB_DIGIT = "\\((.*?)\\)";

    // static String formatStr = "";

    /**
     * @param str 原始需要截取的字符串，如：真正的filename或其他
     * @param reg 表达式,如：${filename}.split("~")[0] <br>
     *            ${filename}.substring(0,17).split(".")[0] <br>
     *            ${filename}.split("~")[1].substring(14,28)
     * @return
     * @Title: formatString
     * @Description: 根据表过式转换字符串
     * @throws:
     */
    public static void formatString(String str, String reg, List<String> res) {
        // 如果表达式是以${filename}开头
        if (reg.startsWith(FILE_NAME_REG)) {
            reg = reg.replace(FILE_NAME_REG, "");
            // formatStr = str;
            res.add(str);
            if (!"".equals(reg)) {
                formatString(str, reg, res);
            }
        } else if (reg.startsWith(".split(")) {// 如果表达式以split开头
            // 取表达式中第一个括号中的值，即split中的表达式
            Matcher splitMatcher = Pattern.compile(SP_CHARACTER).matcher(reg);
            boolean splitFind = splitMatcher.find();
            // 第一个split中的表达式
            String splitGroup = "";
            if (splitFind) {
                // 带有括号的
                String splitGroupAll = splitMatcher.group(0);
                // 不带括号的
                splitGroup = splitMatcher.group(1);
                reg = reg.replaceFirst("\\.split" + string2Regex(splitGroupAll), "");
            }

            // 取表达式中第一个括号中的值，即split中的第几个的值
            Matcher splitDigitMatcher = Pattern.compile(SP_DIGIT).matcher(reg);
            boolean splitDigitFind = splitDigitMatcher.find();
            int digit = 0;
            if (splitDigitFind) {
                // 带有中括号的
                String splitDigitAll = splitDigitMatcher.group(0);
                // 不带中括号的
                String splitDigit = splitDigitMatcher.group(1);
                digit = Integer.parseInt(splitDigit);
                reg = reg.replaceFirst(string2Regex(splitDigitAll), "");
            }
            str = str.split(string2Regex(splitGroup))[digit];
            res.add(str);
            if (!"".equals(reg)) {
                formatString(str, reg, res);
            }

        } else if (reg.startsWith(".substring(")) {// 如果表达式以split开头
            // 取第一个括号中的值
            Matcher subMatcher = Pattern.compile(SUB_DIGIT).matcher(reg);
            if (subMatcher.find()) {
                String subDigitAll = subMatcher.group(0);
                String subDigit = subMatcher.group(1);
                // int subIndex = reg.indexOf(subDigit);
                if (subDigit.contains(",")) {
                    String[] sds = subDigit.split(",");
                    str = str.substring(Integer.parseInt(sds[0]), Integer.parseInt(sds[1]));
                } else {
                    str = str.substring(Integer.parseInt(subDigit));
                }
                reg = reg.replaceFirst("\\.substring" + string2Regex(subDigitAll), "");
                res.add(str);
                if (!"".equals(reg)) {
                    formatString(str, reg, res);
                }
            }
        }
        // return formatStr;
    }

    /**
     * @param reg
     * @return String
     * @Title: formatRegex
     * @Description: 将普通字符串转换成需要的正则表达式，用于replaceAll replaceFirst, 字符串中的几个特殊字符需要转换
     * @throws:
     */
    public static String string2Regex(String reg) {
        char[] flags = {'.', '(', ')', '[', ']', '{', '}', '$', '^', '\\'};

        char[] chars = reg.toCharArray();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            boolean isContain = ArrayUtils.contains(flags, c);
            if (isContain) {
                if (i == 0 || chars[i - 1] != '\\') {
                    sb.append("\\");
                }
            }
            sb.append(c);
        }

        return sb.toString();
    }

}
